use anyhow::Result;
use serde::{Deserialize, Serialize};

/// Plugin trait that all plugins must implement
pub trait Plugin: std::fmt::Debug + Send + Sync {
    /// Get plugin information
    fn info(&self) -> PluginInfo;
    
    /// Initialize the plugin
    fn initialize(&mut self) -> Result<()>;
    
    /// Shutdown the plugin
    fn shutdown(&mut self) -> Result<()>;
    
    /// Execute a plugin command
    fn execute(&self, command: &str, args: &[String]) -> Result<String>;
}

/// Plugin information structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginInfo {
    pub name: String,
    pub version: String,
    pub description: String,
    pub author: String,
    pub api_version: String,
}

/// Plugin API for interacting with the main application
pub trait PluginApi {
    /// Execute a SQL query
    fn execute_query(&self, sql: &str, database: Option<&str>) -> Result<QueryResult>;
    
    /// Get database schema information
    fn get_schema(&self, database: Option<&str>) -> Result<SchemaInfo>;
    
    /// Log a message
    fn log(&self, level: LogLevel, message: &str);
    
    /// Show a notification to the user
    fn show_notification(&self, title: &str, message: &str, level: NotificationLevel);
    
    /// Get application configuration
    fn get_config(&self, key: &str) -> Option<String>;
    
    /// Set application configuration
    fn set_config(&self, key: &str, value: &str) -> Result<()>;
    
    /// Register a new command
    fn register_command(&self, name: &str, description: &str, handler: CommandHandler) -> Result<()>;
    
    /// Register a new menu item
    fn register_menu_item(&self, path: &str, label: &str, handler: MenuHandler) -> Result<()>;
}

/// Query result structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResult {
    pub columns: Vec<ColumnInfo>,
    pub rows: Vec<Vec<serde_json::Value>>,
    pub rows_affected: Option<u64>,
    pub execution_time_ms: f64,
}

/// Column information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColumnInfo {
    pub name: String,
    pub data_type: String,
    pub nullable: bool,
}

/// Schema information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaInfo {
    pub database_name: String,
    pub tables: Vec<TableInfo>,
    pub views: Vec<ViewInfo>,
}

/// Table information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableInfo {
    pub name: String,
    pub schema: String,
    pub columns: Vec<ColumnInfo>,
    pub row_count: Option<u64>,
}

/// View information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ViewInfo {
    pub name: String,
    pub schema: String,
    pub definition: String,
}

/// Log levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LogLevel {
    Trace,
    Debug,
    Info,
    Warn,
    Error,
}

/// Notification levels
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NotificationLevel {
    Info,
    Warning,
    Error,
    Success,
}

/// Command handler function type
pub type CommandHandler = Box<dyn Fn(&[String]) -> Result<String> + Send + Sync>;

/// Menu handler function type
pub type MenuHandler = Box<dyn Fn() -> Result<()> + Send + Sync>;

/// Plugin context provided to plugins
pub struct PluginContext {
    pub api: Box<dyn PluginApi>,
    pub config: PluginConfig,
}

impl std::fmt::Debug for PluginContext {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("PluginContext")
            .field("config", &self.config)
            .finish_non_exhaustive()
    }
}

/// Plugin configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginConfig {
    pub name: String,
    pub enabled: bool,
    pub settings: std::collections::HashMap<String, serde_json::Value>,
}

/// Plugin capability flags
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginCapabilities {
    pub can_execute_queries: bool,
    pub can_modify_schema: bool,
    pub can_access_files: bool,
    pub can_network_access: bool,
    pub requires_elevated_permissions: bool,
}

/// Plugin manifest structure (typically loaded from plugin.toml)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginManifest {
    pub plugin: PluginInfo,
    pub capabilities: PluginCapabilities,
    pub dependencies: Vec<PluginDependency>,
    pub entry_point: String,
    pub supported_platforms: Vec<String>,
}

/// Plugin dependency
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginDependency {
    pub name: String,
    pub version: String,
    pub optional: bool,
}

/// Plugin lifecycle events
#[derive(Debug, Clone)]
pub enum PluginEvent {
    ApplicationStarted,
    ApplicationShutdown,
    DatabaseConnected(String),
    DatabaseDisconnected(String),
    QueryExecuted(String),
    SchemaChanged(String),
}

/// Plugin event handler trait
pub trait PluginEventHandler {
    fn handle_event(&self, event: PluginEvent) -> Result<()>;
}

/// Plugin registry for managing plugin metadata
#[derive(Debug, Default)]
pub struct PluginRegistry {
    plugins: std::collections::HashMap<String, PluginManifest>,
}

impl PluginRegistry {
    /// Create a new plugin registry
    pub fn new() -> Self {
        Self {
            plugins: std::collections::HashMap::new(),
        }
    }
    
    /// Register a plugin manifest
    pub fn register(&mut self, manifest: PluginManifest) {
        self.plugins.insert(manifest.plugin.name.clone(), manifest);
    }
    
    /// Get a plugin manifest by name
    pub fn get(&self, name: &str) -> Option<&PluginManifest> {
        self.plugins.get(name)
    }
    
    /// List all registered plugins
    pub fn list(&self) -> Vec<&PluginManifest> {
        self.plugins.values().collect()
    }
    
    /// Check if a plugin is registered
    pub fn contains(&self, name: &str) -> bool {
        self.plugins.contains_key(name)
    }
    
    /// Remove a plugin from the registry
    pub fn unregister(&mut self, name: &str) -> Option<PluginManifest> {
        self.plugins.remove(name)
    }
}

/// Plugin factory trait for creating plugin instances
pub trait PluginFactory {
    fn create_plugin(&self, context: PluginContext) -> Result<Box<dyn Plugin>>;
}

/// Macro for implementing the plugin entry point
#[macro_export]
macro_rules! plugin_entry_point {
    ($factory:expr) => {
        #[no_mangle]
        pub extern "C" fn create_plugin(
            context: *const $crate::api::PluginContext
        ) -> *mut dyn $crate::api::Plugin {
            use std::boxed::Box;
            
            if context.is_null() {
                return std::ptr::null_mut();
            }
            
            let context = unsafe { &*context };
            
            match $factory.create_plugin(context.clone()) {
                Ok(plugin) => Box::into_raw(plugin),
                Err(_) => std::ptr::null_mut(),
            }
        }
        
        #[no_mangle]
        pub extern "C" fn destroy_plugin(plugin: *mut dyn $crate::api::Plugin) {
            if !plugin.is_null() {
                unsafe {
                    let _ = Box::from_raw(plugin);
                }
            }
        }
    };
}

impl Default for PluginCapabilities {
    fn default() -> Self {
        Self {
            can_execute_queries: false,
            can_modify_schema: false,
            can_access_files: false,
            can_network_access: false,
            requires_elevated_permissions: false,
        }
    }
}

impl Default for PluginConfig {
    fn default() -> Self {
        Self {
            name: "unknown".to_string(),
            enabled: true,
            settings: std::collections::HashMap::new(),
        }
    }
}
